package fsmsim.dataStructure;

import java.util.Vector;

/**
 * <p>Title: FSMSim</p>
 *
 * <p>Description: Simulatore di macchine a stati finiti.</p>
 *
 * <p>Copyright: Copyright (c) 2008</p>
 *
 * <p>Company: Bollati, Donati, Gabrielli, Peli</p>
 *
 * @author Bollati, Donati, Gabrielli, Peli
 * @version 3.0
 */

public class FSM {
    
    public static final String READ = new String("READ");
    public static final String WRITE = new String("WRITE");
    public static final String DEFAULT = new String("WRITE");
    
    
    /**
     * Contiene transizioni della macchina
     */

    private Container transitions;

    
    /**
     * Contiene stato iniziale della macchina
     */

    private State initalState;
    
    
    /**
     * Contiene la tipologia di macchina
     */
    
    private String fsmType;

    
    /**
     * Contiene stato attuale della macchina
     */

    private State actualState;

    
    /**
     * Variabili per il controllo
     */ 

    private boolean hasFinished;

    
    /**
     * Costruttore
     */

    public FSM() {
        if (Container.CONTAINER_TYPE == 0) {
            transitions = new ContainerVector();
        } else if (Container.CONTAINER_TYPE == 1) {
            transitions = new ContainerArrayList();
        }
        hasFinished = false;
    }

    
    /**
     * Metodo set lista transizioni
     * @param transitions
     */

    public void setTransitions(Container transitions) {
        this.transitions = transitions;
    }

    
    /**
     * Metodo getElement lista transizioni
     * @return
     */

    public Container getTransitions() {
        return this.transitions;
    }

    
    /**
     * Metodo che setta l'attributo hasFinished
     * @param hasFinished
     */

    public void setFinished(boolean hasFinished) {
        this.hasFinished = hasFinished;
    }


    /**
     * Metodo set stato iniziale
     * @param initialState
     */

    public void setInitialState(State initialState) {
        this.initalState = initialState;
        this.actualState = initialState;
    }

    
    /**
     * Metodo getElement stato iniziale
     * @return
     */

    public State getInitialState() {
        return this.initalState;
    }
    
    
    /**
     * Metodo set tipo fsm
     * @param fsmType
     */
    
    public void setType(String fsmType) {
        this.fsmType = fsmType;
    }
    
    
    /**
     * Metodo getElement tipo fsm
     * @return
     */
    
    public String getType() {
        return this.fsmType;
    }

    
    /**
     * Ritorna l'indice dello stato attuale
     * @return
     */
 
    public int getCurrentStateIndex() {
        return this.getStateList().indexOfElement(actualState);
    }

    
    /**
     * Metodo getElement stato attuale
     * @return
     */

    public State getCurrentState() {
        return this.actualState;
    }

    
    /**
     * Aggiunge una transizione in coda alla lista delle transizioni
     * @param transition
     */

    public void addTransition(Transition transition) {
        this.getTransitions().addElement(transition);
    }

    
    /**
     * Ritorna una transizione dato l'indice dalla lista delle transizioni
     * @param index
     * @return
     */

    public Element getTransition(int index) {
        return this.getTransitions().getElement(index);
    }

    
    /**
     * Metodo per sapere se la fsm ha finito di scattare
     * @return
     */

    public boolean hasFinished() {
        return hasFinished;
    }

    
    /**
     * Ritorna una lista degli stati
     * @return
     */

    public Container getStateList() {
        Container stateList = new ContainerVector();
        for (int i = 0 ; i < this.getTransitions().size() ; i++) {
            if (!stateList.containsElement(this.getTransitions().getElement(i).getElement(Transition.INITIAL_STATE)))
                stateList.addElement(this.getTransitions().getElement(i).getElement(Transition.INITIAL_STATE));
            
            if (!stateList.containsElement(this.getTransitions().getElement(i).getElement(Transition.FINAL_STATE)))
                stateList.addElement(this.getTransitions().getElement(i).getElement(Transition.FINAL_STATE));
        }
            return stateList;
    }
    
    
    /**
     * Ritorna una lista degli eventi di uscita
     * @return
     */

    public Container getOutputEventList() {
        Container outputEventList = new ContainerVector();
        for (int i = 0 ; i < this.getTransitions().size() ; i++) {
           if(this.getTransitions().getElement(i).getElement(Transition.OUTPUT_EVENT)!= null) {
               
            if (!outputEventList.containsElement(this.getTransitions().getElement(i).getElement(Transition.OUTPUT_EVENT)))
                outputEventList.addElement(this.getTransitions().getElement(i).getElement(Transition.OUTPUT_EVENT));
           }else outputEventList.addElement(null);
        }
            return outputEventList;
    }
    
    
    /**
     * Ritorna la lista degli eventi di ingresso
     * @return
     */

    public Container getInputEventList() {
        Container inputEventList = new ContainerVector();
        for (int i = 0; i < this.getTransitions().size(); i++) {
            if (this.getTransitions().getElement(i).getElement(Transition.INPUT_EVENT) != null) {

                if (!inputEventList.containsElement(this.getTransitions().getElement(i).getElement(Transition.INPUT_EVENT))) {
                    inputEventList.addElement(this.getTransitions().getElement(i).getElement(Transition.INPUT_EVENT));
                }
            } else {
                inputEventList.addElement(null);
            }
        }
        return inputEventList;
    }
     
    
    /**
     * Serve per trovare le transizioni abilitate della fsm dato lo stato
     * attuale e l'evento attuale
     * @param aEvent
     * @return
     */

    public Container searchEnabledTransitions(Event aEvent) {
        String s = new String(this.getCurrentState().getName() + "|" + 
                aEvent.getName() + "|" + "null" + "null");
        Transition tAux = new Transition(s);
        tAux.addElement(this.getCurrentState());
        tAux.addElement(aEvent);
        tAux.addElement(null);
        tAux.addElement(null);
        return this.getTransitions().searchElement(tAux);
    }

    
    /**
     * Funzione per effettuare lo scatto
     * @param transition
     * @return
     */

    public Event executeTransition(Transition transition) {
        this.actualState = (State)transition.getElement(Transition.FINAL_STATE);
        return (Event)transition.getElement(Transition.OUTPUT_EVENT);
    }

    
    /**
     * Guarda se la macchina è abilitata con un certo evento in ingresso
     * @param aEvent
     * @return
     */

    public boolean isEnabled(Event aEvent) {
        if ((aEvent == null) ) {
            return false;
        }
        return (searchEnabledTransitions(aEvent) != null);
    }
    
    
    /**
     * checkInputEvents
     * @return
     */
    
    public boolean checkInputEvents() {
        for (int i = 0 ; i < getInputEventList().size() ; i++)
                if ((Event)getInputEventList().getElement(i) == null) 
                return false;
        return true;
    }
    
    
    /**
     * checkInternalExternalEvents
     * @return
     */
    
    public boolean checkInternalExternalEvents() {
        for (int i = 0 ; i < getInputEventList().size() ; i++)
            if ((getInputEventList().getElement(i) != null) &&
                    ((Event)(getInputEventList().getElement(i))).isInput())
                return false;
        
        for (int i = 0 ; i < getOutputEventList().size() ; i++)
            if ((getOutputEventList().getElement(i) != null) &&
                    ((Event)(getOutputEventList().getElement(i))).isOutput())
                return false;
        return true;
    }
    
    
    /**
     * isReader
     * @return
     */
    
    public boolean isReader() {
        if (getType().equalsIgnoreCase(READ))
            return true;
        return false;
    }

    
    /**
     * Metodo toString
     * @return
     */
    
    @Override
    public String toString() {
        return this.getCurrentState().toString();
    }

    
    /**
     * Metodo toString per la fsm prende in ingresso il nome della fsm
     * @param name
     * @return
     */
    
    public String toString(String name) {
        String fsm;
        fsm = "Stato Iniziale " + name + ": " + this.getInitialState();
        for (int i = 0; i < this.getInputEventList().size(); i++) {
            System.out.println("evento: " + this.getInputEventList().getElement(i));
        }
        
        if (this.getOutputEventList() != null)
            for (int i = 0; i < this.getOutputEventList().size(); i++) {
                System.out.println("evento: " + this.getOutputEventList().getElement(i));
            }

        for (int i = 0; i < this.getStateList().size(); i++) {
            System.out.println("stato: " + this.getStateList().getElement(i));
        }

        for (int i = 0; i < this.getTransitions().size(); i++) {
            System.out.println(this.transitions.getElement(i).getName());
        }
        return fsm;
    }
    
    
    /**
     * Transform
     * @return
     */
    
    public Vector toVector() {
        Vector states = new Vector();
        for (int i = 0 ; i < this.getStateList().size() ; i++) {
            states.add(this.getStateList().getElement(i));
        }
        return states;
    }
  
}
